﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Brainee.Search;
using System.Diagnostics;

namespace Brainee.Examples.Search
{
  /// <summary>
  /// </summary>
  public class RomaniaTrip : IProblem
  {

    public enum City
    {
      Arad,
      Bucharest,
      Craiova,
      Drobeta,
      Eforie,
      Fagaras,
      Giurgiu,
      Hirsova,
      Iasi,
      Lugoj,
      Mehadia,
      Neamt,
      Oradea,
      Pitesti,
      RimnicuVilcea,
      Sibiu,
      Timisoara,
      Urziceni,
      Vaslui,
      Zerind,
    }

#region States

    [DebuggerDisplay ("In({City})")]
    public class In : IState 
    {
      public City City { get; private set; }
      public In (City c) { City = c; }
    }

    private class StateEqualityComparer : IEqualityComparer<IState>
    {
      public bool Equals (IState x, IState y)
      {
        return ((In)x).City == ((In)y).City;
      }

      public int GetHashCode (IState obj)
      {
        return (int)((In)obj).City;
      }
    }

#endregion
#region Actions

    [DebuggerDisplay("Go({City})")]
    public class Go : IAction 
    {
      public City City { get; private set; }
      public Go (City c) { City = c; }
    }

#endregion
#region Roads

    private struct Road
    {
      public City A;
      public City B;
      public Double Distance;
    }

    private List<Road> _roads = new List<Road> () {
      new Road() { A=City.Arad, B=City.Sibiu , Distance=140 },
      new Road() { A=City.Arad, B=City.Timisoara, Distance=118 },
      new Road() { A=City.Arad, B=City.Zerind, Distance=75 },
      new Road() { A=City.Bucharest, B=City.Fagaras, Distance=211 },
      new Road() { A=City.Bucharest, B=City.Giurgiu, Distance=90 },
      new Road() { A=City.Bucharest, B=City.Pitesti, Distance=101 },
      new Road() { A=City.Bucharest, B=City.Urziceni, Distance=85 },
      new Road() { A=City.Craiova, B=City.Drobeta, Distance=120 },
      new Road() { A=City.Craiova, B=City.Pitesti, Distance=138 },
      new Road() { A=City.Craiova, B=City.RimnicuVilcea, Distance=146 },
      new Road() { A=City.Drobeta, B=City.Mehadia, Distance=75 },
      new Road() { A=City.Eforie, B=City.Hirsova, Distance=86 },
      new Road() { A=City.Fagaras, B=City.Sibiu, Distance=99 },
      new Road() { A=City.Hirsova, B=City.Urziceni, Distance=98 },
      new Road() { A=City.Iasi, B=City.Neamt, Distance=87 },
      new Road() { A=City.Iasi, B=City.Vaslui, Distance=92 },
      new Road() { A=City.Lugoj, B=City.Mehadia, Distance=70 },
      new Road() { A=City.Lugoj, B=City.Timisoara, Distance=111 },
      new Road() { A=City.Oradea, B=City.Sibiu, Distance=151 },
      new Road() { A=City.Oradea, B=City.Zerind, Distance=71 },
      new Road() { A=City.Pitesti, B=City.RimnicuVilcea, Distance=97 },
      new Road() { A=City.Urziceni, B=City.Vaslui, Distance=142 },
      new Road() { A=City.RimnicuVilcea, B=City.Sibiu, Distance=80 },
    };

#endregion
#region Constructor

#endregion
#region IProblem implementation

    public IState InitialState { get; set; }

    public IEqualityComparer<IState> StateComparer
    {
      get { return new StateEqualityComparer (); }
    }

    public IEnumerable<ActionState> Successors (IState state)
    {
      List<ActionState> successors = new List<ActionState> ();

      In c = state as In;

      IEnumerable<City> targets = _roads.Where (r => ((r.A == c.City) || (r.B == c.City)))
                                        .Select (r => (r.A == c.City) ? r.B : r.A);
      foreach (City tgt in targets)
      {
        successors.Add (new ActionState (new Go (tgt), new In (tgt)));
      }
      
      return successors;
    }

    public bool GoalTest (IState state)
    {
      In c = state as In;

      return (c.City == City.Bucharest);
    }

    public double EstimatedCostToGoal (IState state)
    {
      In c = state as In;

      switch (c.City)
      {
        case City.Arad: return 366.0;
        case City.Bucharest: return 0.0;
        case City.Craiova: return 160.0;
        case City.Drobeta: return 242.0;
        case City.Eforie: return 161.0;
        case City.Fagaras: return 176.0;
        case City.Giurgiu: return 77.0;
        case City.Hirsova: return 151.0;
        case City.Iasi: return 226.0;
        case City.Lugoj: return 244.0;
        case City.Mehadia: return 241.0;
        case City.Neamt: return 234.0;
        case City.Oradea: return 380.0;
        case City.Pitesti: return 100.0;
        case City.RimnicuVilcea: return 193.0;
        case City.Sibiu: return 253.0;
        case City.Timisoara: return 329.0;
        case City.Urziceni: return 80.0;
        case City.Vaslui: return 199.0;
        case City.Zerind: return 374.0;
      }

      throw new ArgumentOutOfRangeException ();
    }

    public double StepCost (IState from, IAction action, IState to)
    {
      In f = from as In;
      In t = to as In;

      return _roads.First (r => ((r.A == f.City) && (r.B == t.City)) || ((r.B == f.City) && (r.A == t.City)))
                   .Distance;
    }

#endregion
  }
}
